#ifndef JVARIANT_H
#define JVARIANT_H

#include <JXQCore/JMeta>
#include <JXQCore/JStringList>

namespace JXQ
{

class JXQLIB_EXPORT JVariantData
{
public:
	JVariantData() = default;
	JVariantData(const JVariantData &other);
	~JVariantData();

public:
	template <typename TChar>
	typename std::enable_if<__is_char<TChar>::value,
	std::basic_string<TChar>>::type toString() const;

	template <typename TChar>
	typename std::enable_if<__is_wchar<TChar>::value,
	std::basic_string<TChar>>::type toString() const;

	template <typename T>
	typename std::enable_if<__is_primitive<T>::value,T>::type
	tryStringToNumber() const;

	template <typename T, typename TChar>
	typename std::enable_if<__is_primitive<T>::value,T>::type
	tryStringToNumber(const JBasicString<TChar> &str) const;

public:
	const JTypeInfo *typeInfo = &JTypeInfo::voidType;
	void *data = nullptr;
};

// 万能类（var）
class JXQLIB_EXPORT JVariant
{
public:
	template <typename T>
	inline JVariant(T val);
	JVariant();

	// 优先匹配
	JVariant(JVariant &other);
	JVariant(const JVariant &other);
	JVariant(JVariant &&other);
	~JVariant();

public:
	template <typename T>
	inline void set(T val);

	// C风格字符串默认当做JString来处理
	// 如果需要char*，则：set<char*>(...);
	inline void set(char *val);
	inline void set(const char *val);
	inline void set(wchar_t *val);
	inline void set(const wchar_t *val);

public:
	template <typename T>
	inline const JVariant &operator=(T val);

	// 优先匹配
	const JVariant &operator=(JVariant &other);
	const JVariant &operator=(const JVariant &other);
	const JVariant &operator=(JVariant &&other);

public: // 只比较类型
	bool operator==(const JVariant &other) const;
	inline bool operator!=(const JVariant &other) const;

public:
	// 返回类型信息
	const JTypeInfo &type() const;

	// 如果类型是void返回真
	bool isEmpty() const;

	// 数据清零，类型设置为void
	void clear();

public: // 类型转换
	// 预：允许数字和字符串转换（自动识别）
	bool          toBool()       const;
	char          toChar()       const;
	wchar_t       toWChar_t()    const;
	uint8_t       toUChar()      const;
	int16_t       toShort()      const;
	uint16_t      toUShort()     const;
	int           toInt()        const;
	unsigned int  toUint()       const;
	long          toLong()       const;
	unsigned long toULong()      const;
	int64_t       toLLong()      const;
	uint64_t      toULLong()     const;
	float         toFloat()      const;
	double        toDouble()     const;
	long double   toLDouble()    const;

	JBasicString<char> toStringC() const;
	JStringW           toStringW() const;
	JString            toString()  const;

	JBasicStringList<char> toStringCList() const;
	JStringWList           toStringWList() const;
	JStringList            toStringList()  const;

	template <typename T>
	typename std::enable_if<__is_primitive<T>::value,T>::type value() const;

	template <typename T>
	typename std::enable_if<__is_CppStringC<T>::value,T>::type value() const;

	template <typename T>
	typename std::enable_if<__is_CppStringW<T>::value,T>::type value() const;

	template <typename T>
	typename std::enable_if<
		not __is_primitive<T>::value and
		not __is_CppString<T>::value,
	T>::type value() const;

public: // 判断是否可以转换成指定类型
	template <typename T>
	bool canConverted(bool strong = true) const;

private:
	JVariantData *pData;
};

template <typename T>
inline JVariant::JVariant(T val) :
	pData(new JVariantData())
{
	set(std::forward<T>(val));
}

template <typename T>
inline void JVariant::set(T val)
{
	if( std::is_same<typename std::decay<T>::type, JVariant>::value )
	{
		operator=(std::forward<T>(val));
		return ;
	}

	JXQ::JMeta::registerType<typename std::decay<T>::type>(); //注册类型
	if( pData->data == nullptr )
	{
		pData->typeInfo = &JMeta::info(JTypeInfo::getName<typename std::decay<T>::type>());

		// 不要直接拷贝，如果注册出现问题，那么拷贝出来的指针可能无法释放
		pData->data = JMeta::copyObject(pData->typeInfo->name(), &val);
	}
	else
	{
		if( *pData->typeInfo == typeid(typename std::decay<T>::type) )
			JMeta::assignment(pData->typeInfo->name(), pData->data, &val);
		else
		{
			JMeta::deleteObject(pData->typeInfo->name(), pData->data);
			pData->typeInfo = &JMeta::info(JTypeInfo::getName<typename std::decay<T>::type>());
			pData->data = JMeta::copyObject(pData->typeInfo->name(), &val);
		}
	}
}

inline void JVariant::set(char *val)
{ set(JBasicString<char>(val)); }

inline void JVariant::set(const char *val)
{ set(JBasicString<char>(val)); }

inline void JVariant::set(wchar_t *val)
{ set(JBasicString<wchar_t>(val)); }

inline void JVariant::set(const wchar_t *val)
{ set(JBasicString<wchar_t>(val)); }

template <typename T>
inline const JVariant &JVariant::operator=(T val)
{
	set(std::forward<T>(val));
	return *this;
}

inline bool JVariant::operator!=(const JVariant &other) const
{ return operator==(other); }

template <typename TChar>
typename std::enable_if<__is_char<TChar>::value,
std::basic_string<TChar>>::type JVariantData::toString() const
{
	if( typeInfo->isFloat() )
	{
		if( *typeInfo == typeid(float) )
			return JBasicString<TChar>::number(*J_RCT(float*, data)).c_str();
		else if( *typeInfo == typeid(double) )
			return JBasicString<TChar>::number(*J_RCT(double*, data)).c_str();
		else if( *typeInfo == typeid(long double) )
			return JBasicString<TChar>::number(*J_RCT(long double*, data)).c_str();
	}

	else if( typeInfo->isPrimitive() )
	{
		if( not typeInfo->isPointer() )
		{
			if( *typeInfo == typeid(bool) )
				return JBasicString<TChar>::number(*J_RCT(bool*, data)).c_str();

			else if( *typeInfo == typeid(signed short) )
				return JBasicString<TChar>::number(*J_RCT(signed short*, data)).c_str();
			else if( *typeInfo == typeid(unsigned short) )
				return JBasicString<TChar>::number(*J_RCT(unsigned short*, data)).c_str();
			else if( *typeInfo == typeid(signed wchar_t) )
				return JBasicString<TChar>::number(*J_RCT(signed wchar_t*, data)).c_str();
			else if( *typeInfo == typeid(unsigned wchar_t) )
				return JBasicString<TChar>::number(*J_RCT(unsigned wchar_t*, data)).c_str();

			else if( *typeInfo == typeid(signed int) )
				return JBasicString<TChar>::number(*J_RCT(signed int*, data)).c_str();
			else if( *typeInfo == typeid(unsigned int) )
				return JBasicString<TChar>::number(*J_RCT(unsigned int*, data)).c_str();
			else if( *typeInfo == typeid(signed long) )
				return JBasicString<TChar>::number(*J_RCT(signed long*, data)).c_str();
			else if( *typeInfo == typeid(unsigned long) )
				return JBasicString<TChar>::number(*J_RCT(unsigned long*, data)).c_str();

			else if( *typeInfo == typeid(signed long long) )
				return JBasicString<TChar>::number(*J_RCT(signed long long*, data)).c_str();
			else if( *typeInfo == typeid(unsigned long long) )
				return JBasicString<TChar>::number(*J_RCT(unsigned long long*, data)).c_str();

			else if( *typeInfo == typeid(char) or
					 *typeInfo == typeid(signed char) or
					 *typeInfo == typeid(unsigned char) )
				return JBasicString<TChar>("%1").arg(*J_RCT(signed char*, data), true, 10).c_str();
		}
		else
			return JBasicString<TChar>::sprintf(
			#ifdef J_OS_32BIT
						"0x%lX",
			#else
						"0x%LX",
			#endif
						J_RCT(AddressBitsWide, *J_RCT(void**, data))).c_str();
	}
	return std::basic_string<TChar>();
}

template <typename TChar>
typename std::enable_if<__is_wchar<TChar>::value,
std::basic_string<TChar>>::type JVariantData::toString() const
{
	if( typeInfo->isFloat() )
	{
		if( *typeInfo == typeid(float) )
			return JBasicString<TChar>::number(*J_RCT(float*, data)).c_str();
		else if( *typeInfo == typeid(double) )
			return JBasicString<TChar>::number(*J_RCT(double*, data)).c_str();
		else if( *typeInfo == typeid(long double) )
			return JBasicString<TChar>::number(*J_RCT(long double*, data)).c_str();
	}

	else if( typeInfo->isPrimitive() )
	{
		if( not typeInfo->isPointer() )
		{
			if( *typeInfo == typeid(bool) )
				return JBasicString<TChar>::number(*J_RCT(bool*, data)).c_str();
			else if( typeid(TChar) == typeid(signed char) )
				return JBasicString<TChar>::number(*J_RCT(signed char*, data)).c_str();
			else if( *typeInfo == typeid(unsigned char) )
				return JBasicString<TChar>::number(*J_RCT(unsigned char*, data)).c_str();

			else if( *typeInfo == typeid(signed short) )
				return JBasicString<TChar>::number(*J_RCT(signed short*, data)).c_str();
			else if( *typeInfo == typeid(unsigned short) )
				return JBasicString<TChar>::number(*J_RCT(unsigned short*, data)).c_str();

			else if( *typeInfo == typeid(signed int) )
				return JBasicString<TChar>::number(*J_RCT(signed int*, data)).c_str();
			else if( *typeInfo == typeid(unsigned int) )
				return JBasicString<TChar>::number(*J_RCT(unsigned int*, data)).c_str();
			else if( *typeInfo == typeid(signed long) )
				return JBasicString<TChar>::number(*J_RCT(signed long*, data)).c_str();
			else if( *typeInfo == typeid(unsigned long) )
				return JBasicString<TChar>::number(*J_RCT(unsigned long*, data)).c_str();

			else if( *typeInfo == typeid(signed long long) )
				return JBasicString<TChar>::number(*J_RCT(signed long long*, data)).c_str();
			else if( *typeInfo == typeid(unsigned long long) )
				return JBasicString<TChar>::number(*J_RCT(unsigned long long*, data)).c_str();

			else if( *typeInfo == typeid(signed wchar_t) or *typeInfo == typeid(unsigned wchar_t) )
				return JBasicString<TChar>(L"%1").arg(*J_RCT(signed wchar_t*, data), true, 10).c_str();
		}
		else
			return JBasicString<TChar>::sprintf(
			#ifdef J_OS_32BIT
						L"0x%lX",
			#else
						L"0x%LX",
			#endif
						J_RCT(AddressBitsWide, *J_RCT(void**, data))).c_str();
	}
	return std::basic_string<TChar>();
}

template <typename T>
typename std::enable_if<__is_primitive<T>::value,T>::type
JVariantData::tryStringToNumber() const
{
	if( *typeInfo == typeid(char*) or *typeInfo == typeid(const char*) )
		return tryStringToNumber<T,char>(*J_RCT(const char**, data));
	else if( *typeInfo == typeid(std::basic_string<char>) )
		return tryStringToNumber<T,char>(*J_RCT(std::basic_string<char>*, data));
	else if( *typeInfo == typeid(JBasicString<char>) )
		return tryStringToNumber<T,char>(*J_RCT(JBasicString<char>*, data));

	else if( *typeInfo == typeid(const wchar_t*) )
		return tryStringToNumber<T,wchar_t>(*J_RCT(const wchar_t**, data));
	else if( *typeInfo == typeid(std::basic_string<wchar_t>) )
		return tryStringToNumber<T,wchar_t>(*J_RCT(std::basic_string<wchar_t>*, data));
	else if( *typeInfo == typeid(JBasicString<wchar_t>) )
		return tryStringToNumber<T,wchar_t>(*J_RCT(JBasicString<wchar_t>*, data));

	return T();
}

template <typename T, typename TChar>
typename std::enable_if<__is_primitive<T>::value,T>::type
JVariantData::tryStringToNumber(const JBasicString<TChar> &str) const
{
	if( typeid(T) == typeid(float) )
		return str.toFloat(4);
	else if( typeid(T) == typeid(double) )
		return str.toDouble(6);
	else if( typeid(T) == typeid(long double) )
		return str.toLDouble(10);

	else if( typeid(T) == typeid(bool) )
		return str.toInt();
	else if( typeid(T) == typeid(signed char) )
		return str.toInt();
	else if( typeid(T) == typeid(unsigned char) )
		return str.toUInt();

	else if( typeid(T) == typeid(signed short) )
		return str.toShort();
	else if( typeid(T) == typeid(unsigned short) )
		return str.toUShort();
	else if( typeid(T) == typeid(signed wchar_t) )
		return str.toShort();
	else if( typeid(T) == typeid(unsigned wchar_t) )
		return str.toUShort();

	else if( typeid(T) == typeid(signed int) )
		return str.toInt();
	else if( typeid(T) == typeid(unsigned int) )
		return str.toUInt();
	else if( typeid(T) == typeid(signed long) )
		return str.toLong();
	else if( typeid(T) == typeid(unsigned long) )
		return str.toULong();

	else if( typeid(T) == typeid(signed long long) )
		return str.toLLong();
	else if( typeid(T) == typeid(unsigned long long) )
		return str.toULLong();

	return T();
}

template <typename T>
typename std::enable_if<__is_primitive<T>::value,T>::type JVariant::value() const
{
	if( isEmpty() ) //空
		return T();

	const auto &type = this->type();

	// 类型相同 直接转换
	if( type == typeid(T) )
		return *J_RCT(T*, pData->data);

	else if( type.isPrimitive() )
	{
		// 内存一定不会越界，可直接转换
		if( type.size() >= sizeof(T) )
			return *J_RCT(T*, pData->data);
		else // 内存会越界，拷贝一份(能拷多少算多少)
		{
			T t;
			memset(J_SCT(void*, &t), 0, sizeof(T));
			memcpy(J_SCT(void*, &t), pData->data, type.size());
			return t;
		}
	}
	return pData->tryStringToNumber<T>();
}

template <typename T>
typename std::enable_if<__is_CppStringC<T>::value,T>::type JVariant::value() const
{
	if( isEmpty() ) //空
		return T();

	const auto &type = this->type();
	if( type == typeid(T) )
		return *J_RCT(T*, pData->data);

	return pData->toString<char>();
}

template <typename T>
typename std::enable_if<__is_CppStringW<T>::value,T>::type JVariant::value() const
{
	if( isEmpty() ) //空
		return T();

	const auto &type = this->type();
	if( type == typeid(T) )
		return *J_RCT(T*, pData->data);

	return pData->toString<wchar_t>();
}

template <typename T>
typename std::enable_if<
	not __is_primitive<T>::value and
	not __is_CppString<T>::value,
T>::type JVariant::value() const
{
	if( isEmpty() ) //空
		return T();

	const auto &type = this->type();
	if( type == typeid(T) )
		return *J_RCT(T*, pData->data);

	return T();
}

template <typename T>
bool JVariant::canConverted(bool strong) const
{
	if( type() == typeid(T) )
		return true;
	if( strong )
		return false;
	return type().isPrimitive() and JTypeInfo::isPrimitive<T>();
}

class JDebug;
JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const JVariant *var);
JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const JVariant *var);
JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const JVariant &var);
JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const JVariant &var);

} //namespace JXQ


#endif //JVARIANT_H
